﻿// Introduction to Neural Networks for C#, 2nd Edition
// Copyright 2008 by Heaton Research, Inc. 
// http://www.heatonresearch.com/online/introduction-neural-networks-cs-edition-2
//         
// ISBN13: 978-1-60439-009-4  	 
// ISBN:   1-60439-009-3
//   
// This class is released under the:
// GNU Lesser General Public License (LGPL)
// http://www.gnu.org/copyleft/lesser.html

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HeatonResearchNeural.Anneal
{
    abstract public class SimulatedAnnealing<UNIT_TYPE>
    {
        /// <summary>
        /// The starting temperature.
        /// </summary>
        public double StartTemperature
        {
            get
            {
                return startTemperature;
            }
            set
            {
                this.startTemperature = value;
            }
        }

        /// <summary>
        /// The ending temperature.
        /// </summary>
        public double StopTemperature
        {
            get
            {
                return stopTemperature;
            }
            set
            {
                this.stopTemperature = value;
            }
        }

        /// <summary>
        /// The number of cycles that will be used, per iteration.
        /// </summary>
        public int Cycles
        {
            get
            {
                return cycles;
            }
            set
            {
                this.cycles = value;
            }
        }


        /// <summary>
        /// The current error.
        /// </summary>
        public double Error
        {
            get
            {
                return error;
            }
            set
            {
                error = value;
            }
        }

        /// <summary>
        /// The current temperature.
        /// </summary>
        public double Temperature
        {
            get
            {
                return temperature;
            }
        }


        /// <summary>
        /// The starting temperature.
        /// </summary>
        private double startTemperature;

        /// <summary>
        /// The ending temperature.
        /// </summary>
        private double stopTemperature;

        /// <summary>
        /// The number of cycles that will be used, per iteration.
        /// </summary>
        private int cycles;

        /// <summary>
        /// The current error.
        /// </summary>
        private double error;

        /// <summary>
        /// The current temperature.
        /// </summary>
        protected double temperature;

        /// <summary>
        /// Subclasses should provide a method that evaluates the error for the
        /// current solution. Those solutions with a lower error are better. 
        /// </summary>
        /// <returns>Return the error, as a percent.</returns>
        public abstract double DetermineError();
      
        /// <summary>
        /// Subclasses must provide access to an array that makes up the solution. 
        /// </summary>
        /// <returns>An array that makes up the solution.</returns>
        public abstract UNIT_TYPE[] GetArray();

        /// <summary>
        /// Called to perform one cycle of the annealing process.
        /// </summary>
        public void Iteration()
        {
            UNIT_TYPE[] bestArray;

            this.Error = DetermineError();
            bestArray = this.GetArrayCopy();

            this.temperature = this.StartTemperature;

            for (int i = 0; i < this.cycles; i++)
            {
                double curError;
                Randomize();
                curError = DetermineError();
                if (curError < this.Error)
                {
                    bestArray = this.GetArrayCopy();
                    this.Error = curError;
                }

                this.PutArray(bestArray);
                double ratio = Math.Exp(Math.Log(this.StopTemperature
                        / this.StartTemperature)
                        / (this.Cycles - 1));
                this.temperature *= ratio;
            }
        }

        public abstract UNIT_TYPE[] GetArrayCopy();

        public abstract void PutArray(UNIT_TYPE[] array);

        public abstract void Randomize();
    }
}
